<!-- GradientBarZoom.vue -->
<template>
    <div ref="chartRef" class="gradient-bar-zoom"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, computed, unref, onUnmounted } from "vue";
import * as echarts from "echarts/core";
import { DataZoomComponent, TitleComponent, TooltipComponent, GridComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import type { ECBasicOption } from "echarts/types/dist/shared";
import type { MaybeRef } from "vue";

/* 注册所需模块 */
echarts.use([DataZoomComponent, TitleComponent, TooltipComponent, GridComponent, CanvasRenderer]);

/* ---------------- 类型 ---------------- */
interface Props {
    /* 数据 */
    xData?: MaybeRef<string[]>;
    yData?: MaybeRef<number[]>;
    /* 标题 */
    title?: string;
    subTitle?: string;
    /* 主题色 - 可传单个色值，也可传 [16位字符串, 16位字符串] */
    colors?: string | string[];
    /* 点击后缩放的窗口大小（柱子个数） */
    zoomSize?: number;
    /* 是否显示背景阴影柱 */
    showBackground?: boolean;
    /* 图表高度，默认 400 */
    height?: number | string;
}

/* ---------------- props ---------------- */
const props = withDefaults(defineProps<Props>(), {
    xData: () => [],
    yData: () => [],
    title: "",
    subTitle: "",
    colors: () => ["#83bff6", "#188df0"],
    zoomSize: 6,
    showBackground: true,
});

/* ---------------- refs / 实例 ---------------- */
const chartRef = ref<HTMLDivElement>();
let chartInstance: echarts.ECharts;

/* ---------------- option ---------------- */
const option = computed<ECBasicOption>(() => {
    const [topColor, bottomColor] = typeof props.colors === "string" ? [props.colors, props.colors] : props.colors;
    const xData = unref(props.xData);
    const yData = unref(props.yData);
    return {
        title: {
            text: props.title,
            subtext: props.subTitle,
            left: "center",
            show: false,
        },
        grid: { top: "5%", bottom: "15%" },
        xAxis: {
            data:xData,
            axisLabel: { inside: true, color: "#fff" },
            axisTick: { show: false },
            axisLine: { show: false },
            z: 10,
        },
        yAxis: {
            axisLine: { show: false },
            axisTick: { show: false },
            axisLabel: { color: "#999" },
        },
        dataZoom: [{ type: "inside" }],
        series: [
            {
                type: "bar",
                showBackground: props.showBackground,
                backgroundStyle: { color: "rgba(180,180,180,0.1)" },
                itemStyle: {
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                        { offset: 0, color: topColor },
                        { offset: 0.5, color: bottomColor },
                        { offset: 1, color: bottomColor },
                    ]),
                },
                emphasis: {
                    itemStyle: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                            { offset: 0, color: "#2378f7" },
                            { offset: 0.7, color: "#2378f7" },
                            { offset: 1, color: topColor },
                        ]),
                    },
                },
                data: yData,
            },
        ],
    };
});

/* ---------------- 事件 ---------------- */
const emit = defineEmits<{
    (e: "clickBar", params: any): void;
}>();

/* ---------------- 初始化 & 更新 ---------------- */
function initChart() {
    if (!chartRef.value) return;
    chartInstance = echarts.init(chartRef.value);
    chartInstance?.setOption(option.value, true);
    // 点击缩放
    chartInstance.on("click", params => {
        const xData = unref(props.xData);
        const start = Math.max(params.dataIndex - props.zoomSize / 2, 0);
        const end = Math.min(params.dataIndex + props.zoomSize / 2, xData.length - 1);
        chartInstance.dispatchAction({
            type: "dataZoom",
            startValue: xData[start],
            endValue: xData[end],
        });
        emit("clickBar", params);
    });
}

/* 响应式更新 */
watch(option, () => chartInstance?.setOption(option.value));
function resize() {
    chartInstance?.resize();
}
const ro = new ResizeObserver(()=>resize())
onMounted(() => {
    initChart();
    chartRef.value && ro.observe(chartRef.value);
});
onUnmounted(()=>{
    ro.disconnect();
    chartInstance?.dispose();
});
</script>

<style scoped lang="scss">
.gradient-bar-zoom {
    width: 100%;
    height: 100%;
}
</style>
